import io
import json
from datetime import date, datetime
from typing import List, Union, Optional, Dict
import struct
import peewee
from emi.bar.data import Interval
from emi.bar.storage import KVStorage
from emi.bar.storage.model import BaseDBModel
from emi.core.DBSource import DBSource
from emi.trade.data import DailyResult, TradeData, Direction, Offset,OpType
from emi.trade.impl.storage_daily import DailyResultStorage
from emi.trade.impl.storage_position import PositionDataStorage
from emi.trade.trader import PortfolioData
from emi.util import NumUtils
from emi.io import write_float_compress, read_float_compress, write_int_compress, read_int_compress, write_str_list, \
    read_str_list, write_value, read_value
from emi.trade.impl.storage_order import OrderDataStorage


class PortfolioStorage:

    def __init__(self, source: DBSource):
        # indexes_desc = (
        #     (("dimen_value", "interval", "datetime"), True),
        # )
        self._source = source

    @property
    def db_source(self):
        return self._source


    def save(self,name:str,data:PortfolioData):
        db = _PortfolioDataDB(self._source,name)
        db.save(data)

    def load(self,name:str)->PortfolioData:
        db = _PortfolioDataDB(self._source,name)
        return db.load()

    def load_daily_result_list(self,name:str)->List[DailyResult]:
        db = _PortfolioDataDB(self._source,name)
        history_dailys = db.daily_storage.get_all(type= 1)
        return history_dailys

class _PortfolioDataDB:

    VERSION = 0

    def __init__(self,source: DBSource,name:str):
        self._source = source
        self._name = name
        self.order_storage = OrderDataStorage(source,table_name=f"trade_order_{name}")
        self.position_storage = PositionDataStorage(source,table_name=f"trade_position_{name}")
        self.daily_storage = DailyResultStorage(source,table_name=f"trade_daily_{name}")
        self.kv_storage = KVStorage(source,table_name=f"trade_kv_{name}")

    def clear(self):
        self.order_storage.clear()
        self.position_storage.clear()
        self.daily_storage.clear()
        self.kv_storage.clear()

    def save(self,portfolio_data:PortfolioData):
        self.clear()
        output = io.BytesIO()
        output.write(struct.pack("=Bdd",_PortfolioDataDB.VERSION, portfolio_data.available,portfolio_data.commission))
        params_str = json.dumps(portfolio_data.params)
        write_value(output,params_str)
        self.kv_storage.put("meta_data",output.getvalue())
        if portfolio_data.orders:
            self.order_storage.save_bars(portfolio_data.orders)
        if portfolio_data.long_positions:
            self.position_storage.save_bars(list(portfolio_data.long_positions.values()),is_long=True)
        if portfolio_data.short_positions:
            self.position_storage.save_bars(list(portfolio_data.short_positions.values()),is_long=False)

        if portfolio_data.today_daily_result:
            self.daily_storage.save_bars([portfolio_data.today_daily_result],type=0)
        if portfolio_data.daily_results:
            self.daily_storage.save_bars(portfolio_data.daily_results,type=1)

    def load(self)->PortfolioData:
        meta_bytes = self.kv_storage.get_bytes("meta_data",None)
        if not meta_bytes :
            return  None
        offset = 0
        version,available,commission = struct.unpack_from("=Bdd",meta_bytes,offset)

        assert version == 0
        offset+=17
        params_str,offset = read_value(meta_bytes,offset)

        params = json.loads(s=params_str)
        portfolio_data = PortfolioData.of(**params)

        orders = self.order_storage.get_all()
        if orders:
            portfolio_data.orders = orders

        long_positions = self.position_storage.get_all(is_long=True)
        if long_positions:
            _map_value = {}
            for pos in long_positions:
                _map_value[pos.symbol] = pos
            portfolio_data.long_positions = _map_value
        short_positions = self.position_storage.get_all(is_long=False)
        if short_positions:
            _map_value = {}
            for pos in short_positions:
                _map_value[pos.symbol] = pos
            portfolio_data.short_positions = _map_value

        today_dailys = self.daily_storage.get_all(type= 0)
        if today_dailys:
            portfolio_data.today_daily_result = today_dailys[0]
        history_dailys = self.daily_storage.get_all(type= 1)
        if history_dailys:
            portfolio_data.daily_results = history_dailys
        return portfolio_data








